package 多线程;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;

/*
使用Future
在执行多个任务的时候，使用Java标准库提供的线程池是非常方便的。我们提交的任务只需要实现Runnable接口，就可以让线程池去执行：

class Task implements Runnable {
    public String result;

    public void run() {
        this.result = longTimeCalculation(); 
    }
}
Runnable接口有个问题，它的方法没有返回值。如果任务需要一个返回结果，那么只能保存到变量，还要提供额外的方法读取，非常不便。所以，Java标准库还
提供了一个Callable接口，和Runnable接口比，它多了一个返回值：

class Task implements Callable<String> {
    public String call() throws Exception {
        return longTimeCalculation(); 
    }
}
并且Callable接口是一个泛型接口，可以返回指定类型的结果。

现在的问题是，如何获得异步执行的结果？

如果仔细看ExecutorService.submit()方法，可以看到，它返回了一个Future类型，一个Future类型的实例代表一个未来能获取结果的对象：

ExecutorService executor = Executors.newFixedThreadPool(4); 
// 定义任务:
Callable<String> task = new Task();
// 提交任务并获得Future:
Future<String> future = executor.submit(task);
// 从Future获取异步执行返回的结果:
String result = future.get(); // 可能阻塞
当我们提交一个Callable任务后，我们会同时获得一个Future对象，然后，我们在主线程某个时刻调用Future对象的get()方法，就可以获得异步执
行的结果。在调用get()时，如果异步任务已经完成，我们就直接获得结果。如果异步任务还没有完成，那么get()会阻塞，直到任务完成后才返回结果。

一个Future<V>接口表示一个未来可能会返回的结果，它定义的方法有：

get()：获取结果（可能会等待）
get(long timeout, TimeUnit unit)：获取结果，但只等待指定的时间；
cancel(boolean mayInterruptIfRunning)：取消当前任务；
isDone()：判断任务是否已完成。


Future是一个接口，封装了异步计算的结果，源码如下：

public interface Future<V> {
    boolean cancel(boolean mayInterruptIfRunning);
    boolean isCancelled();
    boolean isDone();
    V get() throws InterruptedException, ExecutionException;
    V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}
下面对这五个方法介绍：

（1）booleancancel(boolean mayInterruptIfRunning)

参数mayInterruptIfRunning表示是否允许取消正在执行却没有执行完毕的任务，如果设置true，则表示可以取消正在执行过程中的任务。
·如果任务已经完成，则无论mayInterruptIfRunning为true还是false，此方法肯定返回false，即如果取消已经完成的任务会返回false；
·如果任务正在执行，若mayInterruptIfRunning设置为true，则返回true，若mayInterruptIfRunning设置为false，则返回false；
·如果任务还没有执行，则无论mayInterruptIfRunning为true还是false，肯定返回true。（讲道理这我都没细看，用到时再看）。

（2）boolean isCancelled()

isCancelled方法表示任务是否被取消成功，如果在任务正常完成前被取消成功，则返回 true。

（3）boolean isDone()

isDone方法表示任务是否已经完成，若任务完成，则返回true；

（4）V get() throws InterruptedException, ExecutionException

get()方法用来获取执行结果，这个方法会产生阻塞，会一直等到任务执行完毕才返回；

（5） V get(long timeout, TimeUnit unit)throws InterruptedException, ExecutionException, TimeoutException

get(long timeout, TimeUnit unit)用来获取执行结果，如果在指定时间内，还没获取到结果，就直接返回null。

注意两个get方法都会抛出异常。

3、FutureTask
Future是一个接口，FutureTask是Future接口的唯一实现类，一般用的时候向上转型，使用Future。源码如下：

我们来看一下 FutureTask 的实现：
public class FutureTask<V> implements RunnableFuture<V> {
...
}

FutureTask类实现的是RunnableFuture<V>接口，该接口的源码如下：
public interface RunnableFuture<V> extends Runnable, Future<V> {
    void run();
}
该接口继承了Runnable接口和Future接口，因此FutureTask类既可以当做线程任务递交给线程池执行，又能当Callable任务的计算结果。

FutureTask 提供了 2 个构造器：
public FutureTask(Callable<V> callable) {
}
public FutureTask(Runnable runnable, V result) {
}

当需要异步执行一个计算并在稍后的某个时间点获取其结果时，就可以使用 FutureTask。来个例子:
// 创建一个固定大小的线程池
ExecutorService executorService = Executors.newFixedThreadPool(3);

// 创建一系列 Callable
Callable<Integer>[] tasks = new Callable[5];
for (int i = 0; i < tasks.length; i++) {
    final int index = i;
    tasks[i] = new Callable<Integer>() {
        @Override
        public Integer call() throws Exception {
            TimeUnit.SECONDS.sleep(index + 1);
            return (index + 1) * 100;
        }
    };
}

// 将 Callable 包装为 FutureTask，并提交到线程池
FutureTask<Integer>[] futureTasks = new FutureTask[tasks.length];
for (int i = 0; i < tasks.length; i++) {
    futureTasks[i] = new FutureTask<>(tasks[i]);
    executorService.submit(futureTasks[i]);
}

// 获取任务结果
for (int i = 0; i < futureTasks.length; i++) {
    System.out.println("Result of task" + (i + 1) + ": " + futureTasks[i].get());
}

// 关闭线程池
executorService.shutdown();

来看一下输出结果:
Result of task1: 100
Result of task2: 200
Result of task3: 300
Result of task4: 400
Result of task5: 500

4、使用示例
4.1 Callable + Future
实现Callable接口创建一个异步任务的类，在主线程中起一个线程池执行异步任务，然后在主线程里拿到异步任务的返回结果。
*/
/*
public class 使用Future {
    public static void main(String[] args) {
        ExecutorService threadPool = Executors.newFixedThreadPool(5);// 初始化线程池
        MyTask myTask = new MyTask(5);// 初始化线程任务
        Future<Integer> result = threadPool.submit(myTask);// 向线程池递交线程任务
        // 主线程休眠2秒，模拟主线程在做其他的事情
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 主线程获取异步任务的执行结果
        try {
            System.out.println("异步线程任务执行结果 " + result.get());
            System.out.println("检查异步线程任务是否执行完毕 " + result.isDone());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }

    // 静态内部类，实现Callable接口的任务类
    static class MyTask implements Callable<Integer> {
        private int num;

        public MyTask(int num) {
            this.num = num;
        }

        @Override
        public Integer call() throws Exception {
            for (int i = 1; i < 10; ++i) {
                this.num *= i;
            }
            return this.num;
        }
    }
}
*/
/*
执行结果如下：

异步线程任务执行结果 1814400
检查异步线程任务是否执行完毕 true
4.2 Callable + FutureTask
要做的事情跟4.1一样。
*/

public class 使用Future {
    public static void main(String[] args) {
        ExecutorService threadPool = Executors.newFixedThreadPool(5);// 初始化线程池
        MyTask myTask = new MyTask(5);// 初始化MyTask实例
        FutureTask<Integer> myFutureTask = new FutureTask<>(myTask);// 用MyTask的实例对象初始化一个FutureTask对象
        threadPool.submit(myFutureTask);// 向线程池递交线程任务
        threadPool.shutdown();// 关闭线程池
        // 主线程休眠2秒，模拟主线程在做其他的事情
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 主线程获取异步任务的执行结果
        try {
            System.out.println("异步线程任务执行结果 " + myFutureTask.get());
            System.out.println("检查异步线程任务是否执行完毕 " + myFutureTask.isDone());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

    }

    // 静态内部类，实现Callable接口的任务类
    static class MyTask implements Callable<Integer> {
        private int num;

        public MyTask(int num) {
            this.num = num;
        }

        @Override
        public Integer call() throws Exception {
            System.out.println(Thread.currentThread().getName() + " 正在执行异步任务");
            for (int i = 1; i < 10; ++i) {
                this.num *= i;
            }
            return this.num;
        }
    }
}

/*
执行结果与4.1一样。

要注意FutureTask与Future的使用区别，4.2里的FutureTask对象需要一个实现了Callabel接口的类的对象初始化，FutureTask对象向线程
池submit的时候没有Future<V>的返回值，而Future有，此外由于FutureTask对象既可以当做线程任务递交给线程池执行，又可以当做Future拿
到异步任务的返回结果，因此 FutureTask对象可以直接get得到异步任务的执行结果。

Future与FutureTask的区别：

1.Future是一个接口，FutureTask是一个实现类；
2.使用Future初始化一个异步任务结果一般需要搭配线程池的submit，且submit方法有返回值；而初始化一个FutureTask对象需要传入一个实现了Callable接口的类的对象，直接将FutureTask对象submit给线程池，无返回值；
3.Future + Callable获取结果需要Future对象的get，而FutureTask获取结果直接用FutureTask对象的get方法即可

/*
Runnable和Callable都是Java中用来实现多线程的接口。它们都表示可以在一个单独的线程中执行的代码块。然而，它们之间有一些区别。
	Runnable接口只有一个无返回值的run() 方法。它用于定义一个要在单独线程中执行的任务。当线程执行 run()方法时，它将运行任务，但不会返回任何结果。
因此， Runnable接口更适合用于不需要返回结果的简单任务。
　　Callable接口也是用于定义可以在单独线程中执行的任务，但是它具有不同的方法签名。它的call()方法可以返回一个值，并且可以抛出异常。因此， Callable接
口更适合需要返回结果或可能抛出异常的任务。
下面是一个简单的代码演示，展示如何使用Runnable和Callable接口。
import java.util.concurrent.*;

public class Example {
    
    public static void main(String[] args) throws Exception {
        
        // Create a thread pool with a single thread
        ExecutorService executor = Executors.newSingleThreadExecutor();
        
        // Define a task using a Runnable
        Runnable task1 = () -> {
            System.out.println("Task 1 is running");
        };
        
        // Define a task using a Callable
        Callable<Integer> task2 = () -> {
            System.out.println("Task 2 is running");
            return 42;
        };
        
        // Submit the tasks to the executor
        Future<?> future1 = executor.submit(task1);
        Future<Integer> future2 = executor.submit(task2);
        
        // Wait for the tasks to complete and print their results
        System.out.println("Result of task 1: " + future1.get()); // Prints "Result of task 1: null"
        System.out.println("Result of task 2: " + future2.get()); // Prints "Result of task 2: 42"
        
        // Shut down the executor
        executor.shutdown();
    }
}　　
在这个例子中，我们创建了一个单线程的线程池，并分别定义了一个Runnable和一个 Callable任务。我们将这些任务提交到线程池中，并使用Future对象来
跟踪任务的执行和返回值。最后，我们等待任务完成并打印它们的结果。在任务完成后，我们关闭线程池。注意到，task1并不返回任何值，因此我们在等待结果
时只能得到null。相反，task2返回一个整数值，因此我们可以通过future2.get()得到这个值。
*/